(function($) {
    var doc = document;
    var jsTreeContextMenu = {
        hide_on_mouseleave : false,
        $wrapper : $("<div id='vakata-contextmenu' />"),
        vis : false,
        tgt : false,
        par : false,
        func : false,
        data : false,
        rtl : false,
        show : function(s, t, x, y, d, p, rtl) {            
            var _this = this;
                docWidth = $(doc).width(),
                docHeight = $(doc).height();            
            
            _this.rtl = !!rtl;
            var html = _this.parse(s), h, w;
            if (!html) {
                return;
            }
            _this.vis = true;
            _this.tgt = t;
            _this.par = p || t || null;
            _this.data = d || null;
            _this.$wrapper.html(html).css({
                "visibility" : "hidden",
                "display" : "block",
                "left" : 0,
                "top" : 0
            });

            if (_this.hide_on_mouseleave) {
                _this.$wrapper.one("mouseleave", function(e) {
                    _this.hide();
                });
            }

            h = _this.$wrapper.height();
            w = _this.$wrapper.width();
            if (x + w > docWidth) {
                x = docWidth - (w + 5);
                _this.$wrapper.find("li > ul").addClass("right");
            }
            if (y + h > docHeight) {
                y = y - (h + t[0].offsetHeight);
                _this.$wrapper.find("li > ul").addClass("bottom");
            }

            _this.$wrapper.css({
                "left" : x,
                "top" : y
            }).find("li:has(ul)").bind("mouseenter", function(e) {
                var w = docWidth, h = docHeight, ul = $(this).children("ul").show();
                if (w !== docWidth) {
                    ul.toggleClass("right");
                }
                if (h !== docHeight) {
                    ul.toggleClass("bottom");
                }
            }).bind("mouseleave", function(e) {
                $(this).children("ul").hide();
            }).end().css({
                "visibility" : "visible"
            }).show();
            $(doc).triggerHandler("context_show.vakata");
        },
        hide : function() {
            this.vis = false;
            this.$wrapper.attr("class", "").css({
                "visibility" : "hidden"
            });
            $(doc).triggerHandler("context_hide.vakata");
        },
        parse : function(s, is_callback) {
            var _this = this;
            if (!s) {
                return false;
            }
            var str = "", tmp = false, was_sep = true;
            if (!is_callback) {
                this.func = {};
            }
            str += "<ul>";
            $.each(s, function(i, val) {
                if (!val) {
                    return true;
                }
                _this.func[i] = val.action;
                if (!was_sep && val.separator_before) {
                    str += "<li class='vakata-separator vakata-separator-before'></li>";
                }
                was_sep = false;
                str += "<li class='" + (val._class || "") + (val._disabled ? " jstree-contextmenu-disabled " : "") + "'><ins ";
                if (val.icon && val.icon.indexOf("/") === -1) {
                    str += " class='" + val.icon + "' ";
                }
                if (val.icon && val.icon.indexOf("/") !== -1) {
                    str += " style='background:url(" + val.icon + ") center center no-repeat;' ";
                }
                str += ">&#160;</ins><a href='#' rel='" + i + "'>";
                if (val.submenu) {
                    str += "<span style='float:" + (_this.rtl ? "left" : "right") + ";'>&raquo;</span>";
                }
                str += val.label + "</a>";
                if (val.submenu) {
                    tmp = _this.parse(val.submenu, true);
                    if (tmp) {
                        str += tmp;
                    }
                }
                str += "</li>";
                if (val.separator_after) {
                    str += "<li class='vakata-separator vakata-separator-after'></li>";
                    was_sep = true;
                }
            });
            str = str.replace(/<li class\='vakata-separator vakata-separator-after'\><\/li\>$/, "");
            str += "</ul>";
            $(doc).triggerHandler("context_parse.vakata");
            return str.length > 10 ? str : false;
        },
        exec : function(i) {
            var _this = this;
            if ($.isFunction(_this.func[i])) {
                // if is string - eval and call it!
                _this.func[i].call(_this.data, _this.par);
                return true;
            } else {
                return false;
            }
        }
    };
    
    $(function() {   
             
        jsTreeContextMenu.$wrapper.delegate("a", "click", function(e) {
            e.preventDefault();
        }).delegate("a", "mouseup", function(e) {
            if (!$(this).parent().hasClass("jstree-contextmenu-disabled") && jsTreeContextMenu.exec($(this).attr("rel"))) {
                jsTreeContextMenu.hide();
            } else {
                $(this).blur();
            }
        }).delegate("a", "mouseover", function() {
            jsTreeContextMenu.$wrapper.find(".vakata-hover").removeClass("vakata-hover");
        }).appendTo("body");
        
        $(doc).bind("mousedown", function(e) {
            if (jsTreeContextMenu.vis && !$.contains(jsTreeContextMenu.$wrapper[0], e.target)) {
                jsTreeContextMenu.hide();
            }
        });
        
        //注册快捷按键。
        if (typeof $.hotkeys !== "undefined") {
            $(doc).bind("keydown", "up", function(e) {
                if (jsTreeContextMenu.vis) {
                    var o = jsTreeContextMenu.$wrapper.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").prevAll("li:not(.vakata-separator)").first();
                    if (!o.length) {
                        o = jsTreeContextMenu.$wrapper.find("ul:visible").last().children("li:not(.vakata-separator)").last();
                    }
                    o.addClass("vakata-hover");
                    e.stopImmediatePropagation();
                    e.preventDefault();
                }
            }).bind("keydown", "down", function(e) {
                if (jsTreeContextMenu.vis) {
                    var o = jsTreeContextMenu.$wrapper.find("ul:visible").last().children(".vakata-hover").removeClass("vakata-hover").nextAll("li:not(.vakata-separator)").first();
                    if (!o.length) {
                        o = jsTreeContextMenu.$wrapper.find("ul:visible").last().children("li:not(.vakata-separator)").first();
                    }
                    o.addClass("vakata-hover");
                    e.stopImmediatePropagation();
                    e.preventDefault();
                }
            }).bind("keydown", "right", function(e) {
                if (jsTreeContextMenu.vis) {
                    jsTreeContextMenu.$wrapper.find(".vakata-hover").children("ul").show().children("li:not(.vakata-separator)").removeClass("vakata-hover").first().addClass("vakata-hover");
                    e.stopImmediatePropagation();
                    e.preventDefault();
                }
            }).bind("keydown", "left", function(e) {
                if (jsTreeContextMenu.vis) {
                    jsTreeContextMenu.$wrapper.find(".vakata-hover").children("ul").hide().children(".vakata-separator").removeClass("vakata-hover");
                    e.stopImmediatePropagation();
                    e.preventDefault();
                }
            }).bind("keydown", "esc", function(e) {
                jsTreeContextMenu.hide();
                e.preventDefault();
            }).bind("keydown", "space", function(e) {
                jsTreeContextMenu.$wrapper.find(".vakata-hover").last().children("a").click();                    
            });
        }
    });

    $.jstree.plugin("contextmenu", {
        __init : function() {
            this.get_container().delegate("a", "contextmenu.jstree", $.proxy(function(e) {
                e.preventDefault();
                if (!$(e.currentTarget).hasClass("jstree-loading")) {
                    this.show_contextmenu(e.currentTarget, e.pageX, e.pageY);
                }
            }, this)).delegate("a", "click.jstree", $.proxy(function(e) {
                if (this.data.contextmenu) {
                    jsTreeContextMenu.hide();
                }
            }, this)).bind("destroy.jstree", $.proxy(function() {
                // TODO: move this to descruct method
                if (this.data.contextmenu) {
                    jsTreeContextMenu.hide();
                }
            }, this));            
            $(doc).bind("context_hide.vakata", $.proxy(function() {
                this.data.contextmenu = false;
            }, this));
        },
        defaults : {
            select_node : false,    // requires UI plugin
            show_at_node : true,
            items : {
                "create":{
                    "separator_before":false,
                    "separator_after":true,
                    "_disabled":false,
                    "label":"Create",
                    "action" : function(obj) {                    	
                        this.create(obj);
                    }
                },
                "rename":{
                    "separator_before":false,
                    "separator_after":false,
                    "_disabled":false,
                    "label":"Rename",
                    action : function(obj){
                        this.rename(obj);
                    }
                },
                "remove":{
                    "separator_before":false,
                    "icon":false,
                    "separator_after":false,
                    "_disabled":false,
                    "label":"Delete",
                    "action" : function(obj) {
                        if (this.is_selected(obj)) {
                            this.remove();
                        } else {
                            this.remove(obj);
                        }
                    }
                },
                "ccp":{
                    "separator_before":true,
                    "icon":false,
                    "separator_after":false,
                    "label":"Edit",
                    "action":false,
                    "submenu":{
                        "cut":{
                            "separator_before":false,
                            "separator_after":false,
                            "label":"Cut",
                            "action" : function(obj) {
                                this.cut(obj);
                            }
                        },
                        "copy":{
                            "separator_before":false,
                            "icon":false,
                            "separator_after":false,
                            "label":"Copy",
                            "action" : function(obj) {
                                this.copy(obj);
                            }
                        },
                        "paste":{
                            "separator_before":false,
                            "icon":false,
                            "separator_after":false,
                            "label":"Paste",
                            "action" : function(obj) {
                                this.paste(obj);
                            }
                        }
                    }
                }
            }
        },
        _fn : {
            show_contextmenu : function(obj, x, y) {
                obj = this._get_node(obj);
                var s = this.get_settings().contextmenu, 
                    a = obj.children("a:visible:eq(0)"), 
                    o = false, 
                    i = false;
                    
                if (s.select_node && this.data.ui && !this.is_selected(obj)) {
                    this.deselect_all();
                    this.select_node(obj, true);
                }
                if (s.show_at_node || typeof x === "undefined" || typeof y === "undefined") {
                    o = a.offset();
                    x = o.left;
                    y = o.top + this.data.core.li_height;
                }
                i = obj.data("jstree") && obj.data("jstree").contextmenu ? obj.data("jstree").contextmenu : s.items;
                if ($.isFunction(i)) {
                    i = i.call(this, obj);
                }
                this.data.contextmenu = true;
                jsTreeContextMenu.show(i, a, x, y, this, obj, this._get_settings().core.rtl);
                if (this.data.themes) {
                    jsTreeContextMenu.$wrapper.attr("class", "jstree-" + this.data.themes.theme + "-context");
                }
            }
        }
    });
})(jQuery);